package wowraidmanager.client.auction.data.canvas;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;

import wowraidmanager.client.auction.data.calculate.CalculateData;
import wowraidmanager.shared.model.auction.canvas.AuctionCalculateData;
import wowraidmanager.shared.model.auction.canvas.TooltipData;

import com.google.gwt.canvas.client.Canvas;
import com.google.gwt.canvas.dom.client.Context2d;
import com.google.gwt.canvas.dom.client.Context2d.TextAlign;
import com.google.gwt.canvas.dom.client.Context2d.TextBaseline;
import com.google.gwt.canvas.dom.client.CssColor;
import com.google.gwt.event.dom.client.MouseMoveEvent;
import com.google.gwt.event.dom.client.MouseMoveHandler;
import com.google.gwt.i18n.client.DateTimeFormat;

public class DrawGraphCanvas extends BaseDrawCanvas{

    ArrayList<AuctionCalculateData> allianceAuctionCalculateDataList;
    ArrayList<AuctionCalculateData> hordeAuctionCalculateDataList;
    ArrayList<AuctionCalculateData> neutralAuctionCalculateDataList;

    ArrayList<Date> dateList;

    Canvas tooltipCanvas;
    Context2d tooltipContext;

    long maximumPrice;
    long minimumPrice;

    int graphWidth = 250;
    int graphHeight = 200;

    int graphBeginX = 100;
    int graphBeginY = 50;

    int betweenWidth;

    static DateTimeFormat DATE_FORMAT = DateTimeFormat.getFormat("yyyy/MM/dd");
    static DateTimeFormat DATE_FORMATMMDD = DateTimeFormat.getFormat("MM/dd");

    CalculateData calculateData = new CalculateData();

    public DrawGraphCanvas(Canvas dataCanvas, Canvas tooltipCanvas){
        super(dataCanvas);
        this.tooltipCanvas = tooltipCanvas;
        this.tooltipContext = tooltipCanvas.getContext2d();
    }

    public void setAuctionData(ArrayList<AuctionCalculateData> allianceAuctionCalculateData, ArrayList<AuctionCalculateData> hordeAuctionCalculateData, ArrayList<AuctionCalculateData> neutralAuctionCalculateData){
        this.allianceAuctionCalculateDataList = allianceAuctionCalculateData;
        this.hordeAuctionCalculateDataList = hordeAuctionCalculateData;
        this.neutralAuctionCalculateDataList = neutralAuctionCalculateData;

        if(this.allianceAuctionCalculateDataList.size() == 0 && this.hordeAuctionCalculateDataList.size() == 0 && this.neutralAuctionCalculateDataList.size() == 0){
            setClear();
        }else{
            setInit();
            checkPrice();
            drawBaseGraph();
            setTooltip();
            lineGraph();
        }
    }

    private void setTooltip(){
        tooltipDataList = new ArrayList<TooltipData>();
        tooltipCanvas.addMouseMoveHandler(new MouseMoveHandler(){

            public void onMouseMove(MouseMoveEvent event){
                checkTooltipPosition(event.getX(), event.getY());
            }
        });

    }

    protected void checkTooltipPosition(int x, int y){
        boolean isContain = false;
        for(TooltipData tooltipData:tooltipDataList){
            if(tooltipData.getPositionStartX() < x && tooltipData.getPositionStartY() < y && tooltipData.getPositionEndX() > x && tooltipData.getPositionEndY() > y){
                drawTooltip(x, y, tooltipData.getMessage());
                isContain = true;
                break;
            }
        }
        if(isContain == false){
            tooltipContext.clearRect(0, 0, 400, 300);
        }
    }

    private void drawTooltip(int x, int y, String message){
        tooltipContext.clearRect(0, 0, 400, 300);
        double stringWidth = tooltipContext.measureText(message).getWidth();
        boolean isOverWidth = false;

        if(stringWidth + x + 20 > 400){
            isOverWidth = true;
        }


        tooltipContext.setFont("10px 'Helvetica'");
        tooltipContext.beginPath();
        tooltipContext.setLineWidth(1);
        tooltipContext.setStrokeStyle("black");
        tooltipContext.setFillStyle("white");
        if(isOverWidth){
            tooltipContext.rect(x - stringWidth - 5, y -20, stringWidth + 15, 20);
            tooltipContext.setTextAlign(TextAlign.RIGHT);
        }else{
            tooltipContext.rect(x + 5, y - 20, stringWidth + 15, 20);
            tooltipContext.setTextAlign(TextAlign.LEFT);
        }
        tooltipContext.fill();
        tooltipContext.stroke();

        tooltipContext.setTextBaseline(TextBaseline.TOP);
        tooltipContext.setFillStyle("black");
        if(isOverWidth){
            tooltipContext.fillText(message, x + 5, y - 15);
        }else{
            tooltipContext.fillText(message, x + 10, y - 15);
        }
    }

    private void lineGraph(){
        if(dateList.size() > 1){
            betweenWidth = graphWidth / (dateList.size() - 1);
        }else{
            betweenWidth = 0;
        }

        drawLineGraph(allianceAuctionCalculateDataList, allianceAverageColor, allianceMinimumColor);
        drawLineGraph(hordeAuctionCalculateDataList, hordeAverageColor, hordeMinimumColor);
        drawLineGraph(neutralAuctionCalculateDataList, neutralAverageColor, neutralMinimumColor);
    }

    private void drawLineGraph(ArrayList<AuctionCalculateData> auctionCalculateDataList, CssColor averageColor, CssColor minimumColor){
        int column = 0;
        boolean isBegin = false;
        dataContext.setLineWidth(3);
        dataContext.setFillStyle(averageColor);
        dataContext.setStrokeStyle(averageColor);
        dataContext.beginPath();

        for(Date date:dateList){
            String dateString = DATE_FORMAT.format(date);
            boolean isContain = false;
            for(AuctionCalculateData auctionCalculateData:auctionCalculateDataList){
                if(dateString.equals(DATE_FORMAT.format(auctionCalculateData.getCalculateDate()))){
                    long lineY = getLineY(auctionCalculateData.getAverageBuyoutPrice());
                    if(isBegin == false){
                        dataContext.moveTo(graphBeginX + (column * betweenWidth), graphBeginY + (graphHeight - lineY));
                        dataContext.arc(graphBeginX + (column * betweenWidth), graphBeginY + (graphHeight - lineY), 2, 0, Math.PI * 2);
                        isBegin = true;
                    }else{
                        dataContext.lineTo(graphBeginX + (column * betweenWidth), graphBeginY + (graphHeight - lineY));
                        dataContext.arc(graphBeginX + (column * betweenWidth), graphBeginY + (graphHeight - lineY), 2, 0, Math.PI * 2);
                    }
                    addTooltipData(graphBeginX + (column * betweenWidth), (int)(graphBeginY + (graphHeight - lineY)), calculateData.toGold(auctionCalculateData.getAverageBuyoutPrice()));
                    column++;
                    isContain = true;
                    break;
                }
            }
            if(isContain == false){
                column++;
            }
        }
        dataContext.stroke();
        column = 0;
        isBegin = false;

        dataContext.setFillStyle(minimumColor);
        dataContext.setStrokeStyle(minimumColor);
        dataContext.beginPath();
        for(Date date:dateList){
            String dateString = DATE_FORMAT.format(date);
            boolean isContain = false;
            for(AuctionCalculateData auctionCalculateData:auctionCalculateDataList){
                if(dateString.equals(DATE_FORMAT.format(auctionCalculateData.getCalculateDate()))){
                    long lineY = getLineY(auctionCalculateData.getMinimumBuyoutPrice());
                    if(isBegin == false){
                        dataContext.moveTo(graphBeginX + (column * betweenWidth), graphBeginY + (graphHeight - lineY));
                        dataContext.arc(graphBeginX + (column * betweenWidth), graphBeginY + (graphHeight - lineY), 2, 0, Math.PI * 2);
                        isBegin = true;
                    }else{
                        dataContext.lineTo(graphBeginX + (column * betweenWidth), graphBeginY + (graphHeight - lineY));
                        dataContext.arc(graphBeginX + (column * betweenWidth), graphBeginY + (graphHeight - lineY), 2, 0, Math.PI * 2);
                    }
                    addTooltipData(graphBeginX + (column * betweenWidth), (int)(graphBeginY + (graphHeight - lineY)), calculateData.toGold(auctionCalculateData.getMinimumBuyoutPrice()));
                    column++;
                    isContain = true;
                    break;
                }
            }
            if(isContain == false){
                column++;
            }
        }
        dataContext.stroke();

        column = 0;
        dataContext.setStrokeStyle(textColor);
        for(Date date:dateList){
            if(column != 0 && column != dateList.size() - 1){
                dataContext.beginPath();
                dataContext.rect(graphBeginX + (column * betweenWidth) - 2, 268, 4, 4);
                addTooltipData(graphBeginX + (column * betweenWidth), 270, DATE_FORMATMMDD.format(date));
                dataContext.stroke();
            }
            column++;
        }
    }

    private void addTooltipData(int x, int y, String message){
        TooltipData tooltipData = new TooltipData();
        tooltipData.setPositionStartX(x - 5);
        tooltipData.setPositionStartY(y - 5);
        tooltipData.setPositionEndX(x + 5);
        tooltipData.setPositionEndY(y + 5);
        tooltipData.setMessage(message);
        tooltipDataList.add(tooltipData);
    }

    private long getLineY(long targetPrice){
        if(maximumPrice == minimumPrice){
            return 0;
        }else{
            return (targetPrice - minimumPrice) * 200 / (maximumPrice - minimumPrice);
        }
    }

    private void drawBaseGraph(){
        dataContext.setFillStyle(backGroundColor);
        dataContext.beginPath();
        dataContext.fillRect(0, 0, 400, 300);
        dataContext.setStrokeStyle(textColor);
        dataContext.setLineWidth(1);
        dataContext.moveTo(100, 50);
        dataContext.lineTo(100, 250);
        dataContext.lineTo(350, 250);
        dataContext.stroke();

        dataContext.setFillStyle(textColor);
        dataContext.setFont("10px 'Helvetica'");

        dataContext.setTextAlign(TextAlign.RIGHT);
        dataContext.setTextBaseline(TextBaseline.MIDDLE);
        if(maximumPrice != Long.MIN_VALUE && minimumPrice != Long.MAX_VALUE){
            dataContext.fillText(calculateData.toGold(maximumPrice), 95, 50);
            dataContext.fillText(calculateData.toGold((maximumPrice + minimumPrice) / 2), 95, 150);
            dataContext.fillText(calculateData.toGold(minimumPrice), 95, 250);
        }

        dataContext.setTextAlign(TextAlign.CENTER);
        int dateSize = dateList.size();
        if(dateSize > 1){
            dataContext.fillText(DATE_FORMATMMDD.format(dateList.get(0)), 100, 270);
            dataContext.fillText(DATE_FORMATMMDD.format(dateList.get(dateSize - 1)), 350, 270);
        }else if(dateSize == 1){
            dataContext.fillText(DATE_FORMATMMDD.format(dateList.get(0)), 100, 270);
        }
        // dataContext.closePath();
    }

    private void checkPrice(){
        dateList = new ArrayList<Date>();
        checkMaxMinPrice(allianceAuctionCalculateDataList);
        checkMaxMinPrice(hordeAuctionCalculateDataList);
        checkMaxMinPrice(neutralAuctionCalculateDataList);
        Collections.sort(dateList, new DateComparator());
    }

    private void checkMaxMinPrice(ArrayList<AuctionCalculateData> auctionCalculateDataList){
        for(AuctionCalculateData auctionCalculateData:auctionCalculateDataList){
            if(auctionCalculateData.getAverageBuyoutPrice() > maximumPrice){
                maximumPrice = auctionCalculateData.getAverageBuyoutPrice();
            }

            if(auctionCalculateData.getMinimumBuyoutPrice() < minimumPrice){
                minimumPrice = auctionCalculateData.getMinimumBuyoutPrice();
            }

            if(dateList.size() != 0){
                String calculateDate = DATE_FORMAT.format(auctionCalculateData.getCalculateDate());
                boolean isContain = false;

                for(Date date:dateList){
                    if(calculateDate.equals(DATE_FORMAT.format(date))){
                        isContain = true;
                        break;
                    }
                }

                if(isContain == false){
                    dateList.add(auctionCalculateData.getCalculateDate());
                }
            }else{
                dateList.add(auctionCalculateData.getCalculateDate());
            }
        }
    }

    private void setInit(){
        maximumPrice = Long.MIN_VALUE;
        minimumPrice = Long.MAX_VALUE;
    }

    class DateComparator implements Comparator{
        public int compare(Object s, Object t){
            return ((Date)s).compareTo(((Date)t));
        }
    }
}
